﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Effects.Filters;
using Effects.Effects;
using Effects.Effects.GuiParts;
using Effects.Effects.Tools;

namespace EffectsPack
{
    /// <summary>
    ///  Effect uses bandpass filter that is controlled by envelope detector.
    /// </summary>
    public class EnvelopeFilter : Effect
    {
        #region controls
        // user controlled parameters 

        #region footswitch

        /// <summary>
        ///  Footswitch for by passing effect.
        /// </summary>
        private Footswitch footswitch;

        /// <summary>
        ///  Led indicating On/Off state.
        /// </summary>
        private LED led;

        /// <summary>
        ///  Synchornizer for footswitch and led.
        /// </summary>
        private FootswitchLedSynchronizer synchronizer;


        /// <summary>
        ///  Key for footswitch settings.
        /// </summary>
        private int footswitchKey = 0;

        #endregion

        #region pregain
        /// <summary>
        ///  Pre gain - bit like threshold
        /// </summary>
        private double preGain;

        /// <summary>
        ///  Controls pre gain.
        /// </summary>
        private Pot PreGainPot;

        /// <summary>
        ///  Key for pre gain parameter.
        /// </summary>
        private const int preGainKey = 1;

        #endregion

        #region lowerFrequency

        /// <summary>
        ///  Current lower border frequency.
        /// </summary>
        private double lowerFrequency = 500;

        /// <summary>
        ///  Controls lower border frequency.
        /// </summary>
        private Pot LowerFreqPot;

        /// <summary>
        ///  Key for the lower frequency parameter
        /// </summary>
        private const int lowerFrequencyKey = 2;

        #endregion

        #region frequencyRange

        /// <summary>
        ///  Current frequency range of wah.
        /// </summary>
        private double frequencyRange = 5000;

        /// <summary>
        ///  Controls frequency range.
        /// </summary>
        private Pot FrequencyRangePot;

        /// <summary>
        ///  Key for the frequency range parameter.
        /// </summary>
        private const int frequencyRangeKey = 3;

        #endregion

        #region master

        /// <summary>
        ///  Master volume level.
        /// </summary>
        private double master;

        /// <summary>
        ///  Controls master volume level.
        /// </summary>
        private Pot Master;

        /// <summary>
        ///  Key for the master parameter.
        /// </summary>
        private const int masterKey = 4;

        #endregion

        #region BP_LP_switch
        // bandpass lowpass switching

        /// <summary>
        ///  Possible modification, choosing between bandpass and lowpass filtering.
        /// </summary>
        private bool bandPass=true;

        #endregion
       
        #endregion

        #region constructor
        // constructor and initialization

        /// <summary>
        ///  Creates new instance of EQ
        /// </summary>
        public EnvelopeFilter()
        {
            //this.locker = new object();
            BackgroundImage = System.Drawing.Image.FromStream(new System.IO.MemoryStream(EffectsPack.Properties.Resources.EnvelopeFilter));
            Size = BackgroundImage.Size;

            InitializeComponents();
            InitializeFootswitch();
        }

        /// <summary>
        ///  Initializes potentiometers
        /// </summary>
        private void InitializeComponents()
        {
            PreGainPot = new Pot();
            PreGainPot.MaxValue = 10;
            PreGainPot.MinValue = 0;
            PreGainPot.Knob = PotKnob.ChickenKnobMiddle;
            PreGainPot.ValueChanged += new PotValueChangedEventHandler(this.PreGainMoved);
            preGain = 24;
            PreGainPot.Location = new System.Drawing.Point(5, 50);
            Controls.Add(PreGainPot);
            PreGainPot.Reset();

            LowerFreqPot = new Pot();
            LowerFreqPot.MaxValue = 2000;
            LowerFreqPot.MinValue = 200;
            LowerFreqPot.Knob = PotKnob.ChickenKnobMiddle;
            LowerFreqPot.ValueChanged += new PotValueChangedEventHandler(this.LowerFreqMoved);
            LowerFreqPot.Location = new System.Drawing.Point(80, 50);
            Controls.Add(LowerFreqPot);
            LowerFreqPot.Reset();

            FrequencyRangePot = new Pot();
            FrequencyRangePot.MaxValue = 10000;
            FrequencyRangePot.MinValue = 1000;
            FrequencyRangePot.Knob = PotKnob.ChickenKnobMiddle;
            FrequencyRangePot.ValueChanged += new PotValueChangedEventHandler(this.FrequencyRangeMoved);
            FrequencyRangePot.Location = new System.Drawing.Point(150, 50);
            Controls.Add(FrequencyRangePot);
            FrequencyRangePot.Reset();

            Master = new Pot();
            Master.MaxValue = 10;
            Master.MinValue = 0;
            Master.Knob = PotKnob.ChickenKnobMiddle;
            Master.Location = new System.Drawing.Point(225, 50);
            Master.ValueChanged += new PotValueChangedEventHandler(this.MasterMoved);
            Controls.Add(Master);
            Master.Reset();
        }

        /// <summary>
        ///  Iniializes footswitch and led.
        /// </summary>
        private void InitializeFootswitch()
        {
            // footswitch
            footswitch = new Footswitch();
            switchedOn = true;
            footswitch.On = true;
            footswitch.Switched += new FootswitchSwitchedEventHandler(this.FootswitchSwitched);
            footswitch.Location = new System.Drawing.Point(135, 120);
            Controls.Add(footswitch);

            // led
            led = new LED();
            led.On = true;
            led.Location = new System.Drawing.Point(140, 40);
            Controls.Add(led);

            // synchronization
            synchronizer = new FootswitchLedSynchronizer(footswitch, led);
        }

        /// <summary>
        ///  Initializes filters.
        /// </summary>
        private void InitializeEnvelopeFilters()
        {
            double Q = 200 / ((double)sampleRate / 2) * Math.PI;
            padMinQ = (2F - Math.Sqrt(4F - 4F * Math.Cos(Q) * Math.Cos(Q))) / (2F * Math.Cos(Q));

            Q = 200 / ((double)sampleRate / 2) * Math.PI;
            double padMaxQ = (2F - Math.Sqrt(4F - 4F * Math.Cos(Q) * Math.Cos(Q))) / (2F * Math.Cos(Q));
            padRangeQ = padMaxQ - padMinQ;


            envelopeBandPass = new TunableBandPass();
            Q = 400 / ((double)sampleRate / 2) * Math.PI;
            double alfa = (2F - Math.Sqrt(4F - 4F * Math.Cos(Q) * Math.Cos(Q))) / (2F * Math.Cos(Q));

            envelopeBandPass.Alfa = alfa;
            envelopeBandPass.Beta = Math.Cos(5500 / ((double)(sampleRate / 2)) * Math.PI);


            padMinFreq = lowerFrequency / ((double)sampleRate / 2) * Math.PI;
            padRangeFreq = frequencyRange / ((double)sampleRate / 2) * Math.PI;


            envelopeLowPass = new TunableLowPass((int)sampleRate);
            envelopeLowPass.Alfa = envelopeBandPass.Beta;
        }

        #endregion

        #region effects_properties

        /// <summary>
        ///  Time interval between adjacent samples in seconds
        /// </summary>
        private double step;

        /// <summary>
        ///  Gets or sets the sampling rate
        /// </summary>
        public override uint SampleRate
        {
            get { return base.SampleRate; }
            set
            {
                sampleRate = value;
                step = 1F / sampleRate;

                InitializeEnvelopeFilters();

                delay = new Effects.Effects.Tools.ConstantDelayLine(6, (int)sampleRate);
                signalPower = new RMS(2, (int)sampleRate);

            }
        }

        /// <summary>
        ///  Gets the name of the effect.
        /// </summary>
        public override string Name
        {
            get { return "Envelope filter v2.0 alfa"; }
        }

        #endregion

        #region main_work

        /// <summary>
        ///  Measures signal power.
        /// </summary>
        private RMS signalPower;

        /// <summary>
        ///  Compensates delayed results from RMS.
        /// </summary>
        private Effects.Effects.Tools.ConstantDelayLine delay;

        /// <summary>
        ///  Effects input signal.
        /// </summary>
        /// <param name="data"> Array containing input signal. </param>
        /// <returns> Effected output signal </returns>
        public override double[] effect(double[] data)
        {
            double[] outData = new double[data.Length];

            if (switchedOn)
            {
                for (int i = 0; i < data.Length; i++)
                {
                    double rms = signalPower.Step(data[i] * preGain);

                    if (rms > 0.5)
                    {
                        rms -= 0.5;
                        rms *= 2;

                        // tune filters.
                        if (bandPass)
                        {
                            envelopeBandPass.Alfa = padMinQ + rms * padRangeQ;
                            envelopeBandPass.Beta = Math.Cos(padMinFreq + rms * padRangeFreq);
                        }
                        else
                        {
                            envelopeLowPass.Alfa = Math.Cos(padMinFreq + rms * padRangeFreq);
                        }

                    }

                    double sample = delay.Step(data[i]);

                    if (bandPass)
                        outData[i] = master * 0.5 * (sample - envelopeBandPass.Step(sample));
                    else
                        outData[i] = master * 0.5 * (sample + envelopeLowPass.Step(sample));

                }
            }
            else
            {
                for (int i = 0; i < data.Length; i++)
                    outData[i] = delay.Step(data[i]);
            }
            return outData;
        }

        /// <summary>
        ///  wah - filter.
        /// </summary>
        private TunableBandPass envelopeBandPass;
        
        /// <summary>
        ///  Normalized wah lower border frequency
        /// </summary>
        private double padMinFreq;

        /// <summary>
        ///  Normalized wah frequency range
        /// </summary>
        private double padRangeFreq;

        /// <summary>
        ///  Normalized min bandwidth of filter.
        /// </summary>
        private double padMinQ;

        /// <summary>
        ///  Normalized max bandwidth of filter.
        /// </summary>
        private double padRangeQ;


        #endregion

        /// <summary>
        ///  Might be used in future (possible modification is to allow user switching between band-pass and low-pass filter.
        /// </summary>
        private TunableLowPass envelopeLowPass;

        //private object locker;
           
        #region controls_event_handlers
        // handling events from controls
       
        /// <summary>
        ///  Footswitch.Switched event handler. According to the event arguments signal bypasses or runs through the effect.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event Arguments </param>
        private void FootswitchSwitched(object sender, FootswitchSwitchedEventArgs args)
        {
            this.switchedOn = args.On;

            // raise the event
            if (this.SettingsChanged != null)
            {
                EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(footswitchKey, switchedOn);
                this.SettingsChanged(this, changeArgs);
            }
        }

        /// <summary>
        ///  Master potentiometer event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event arguments. </param>
        private void MasterMoved(object sender, PotValueChangedEventArgs args)
        {
            master = args.NewValue;

            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(masterKey, master);
            if (SettingsChanged != null)
                SettingsChanged(this, changeArgs);
        }

        /// <summary>
        ///  Pre gain pot event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event arguments. </param>
        private void PreGainMoved(object sender, PotValueChangedEventArgs args)
        {
            //this.wahFreq = args.NewValueDouble;
            preGain = args.NewValue;
            //InitializeFilters(wahFreq, sampleRate);

            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(preGainKey, preGain);
            if (SettingsChanged != null)
                SettingsChanged(this, changeArgs);
        }

        /// <summary>
        ///  LowerFrequency pot event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event arguments </param>
        private void LowerFreqMoved(object sender, PotValueChangedEventArgs args)
        {
            this.lowerFrequency = args.NewValue;
            //InitializeEnvelopeFilters();
            padMinFreq = lowerFrequency / (sampleRate / (double) 2) * Math.PI;
            //ChangeFreqs();

            
            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(lowerFrequencyKey, lowerFrequency);
            if (SettingsChanged != null)
                SettingsChanged(this, changeArgs); 
        }

        /// <summary>
        ///  FrequencyRange pot event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event arguments </param>
        private void FrequencyRangeMoved(object sender, PotValueChangedEventArgs args)
        {
            this.frequencyRange = args.NewValue;
            //InitializeEnvelopeFilters();
            padRangeFreq = frequencyRange / (sampleRate / (double)2) * Math.PI;
            //ChangeFreqs();

             EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(frequencyRangeKey, frequencyRange);
             if (SettingsChanged != null)
                 SettingsChanged(this, changeArgs); 
        }

        #endregion

        #region settings
        // getting and setting the settings of EQ

        /// <summary>
        ///  Raised when any parameter is changed
        /// </summary>
        public override event EffectSettingsChangedDelegate SettingsChanged;

        /// <summary>
        ///  Gets all current settings of the effect.
        /// </summary>
        /// <returns> All current seetings </returns>
        public override object[] GetAllSettings()
        {
            object[] settings = new object[5];

            settings[0] = switchedOn;
            settings[1] = preGain;
            settings[2] = lowerFrequency;
            settings[3] = frequencyRange;
            settings[4] = master;
            return settings;
        }

        /// <summary>
        ///  Sets all settings.
        /// </summary>
        /// <param name="settings"> All settings of the effect. This argument should be output of GetAllSettings method that has been called previously. </param>
        public override void SetAllSettings(object[] settings)
        {
            switchedOn = (bool)settings[0];
            synchronizer.SetValue(switchedOn);

            preGain = (double)settings[1];
            PreGainPot.SetValue(preGain);

            lowerFrequency = (double)settings[2];
            LowerFreqPot.SetValue(lowerFrequency);

            frequencyRange = (double)settings[3];
            FrequencyRangePot.SetValue(frequencyRange);

            master = (double)settings[4];
            Master.SetValue(master);
        }

        /// <summary>
        ///  Sets one settings
        /// </summary>
        /// <param name="e"> Setting change to be done </param>
        public override void SetSetting(EffectSettingsChangedEventArgs e)
        {
            int key = (int)e.Key;

            if (key == footswitchKey)
            {
                switchedOn = (bool)e.Value;
                synchronizer.SetValue(switchedOn);
                return;
            }
            if (key == preGainKey)
            {
                preGain = (double)e.Value;
                PreGainPot.SetValue(preGain);
                return;
            }
            if (key == lowerFrequencyKey)
            {
                lowerFrequency = (double)e.Value;
                LowerFreqPot.SetValue(lowerFrequency);
                return;
            }
            if (key == frequencyRangeKey)
            {
                frequencyRange = (double)e.Value;
                FrequencyRangePot.SetValue(frequencyRange);
                return;
            }
            if (key == masterKey)
            {
                master = (double)e.Value;
                Master.SetValue(master);
                return;
            }

       }

        /// <summary>
        ///  Gets parameter description.
        /// </summary>
        /// <returns> EffectParam array descrobing effect's parameters. </returns>
        public override EffectParam[] GetParameters()
        {
            EffectParam[] parameters = new EffectParam[5];

            ListItem[] footswitchState = new ListItem[2];
            footswitchState[0] = new ListItem("Turn on", true);
            footswitchState[1] = new ListItem("Turn off", false);
            parameters[0] = new EffectParam(footswitchKey, "ON/OFF", footswitchState);

            parameters[1] = new EffectParam(preGainKey, "Pre gain", 0F, 10F);
            parameters[2] = new EffectParam(lowerFrequencyKey, "Low border frequency", 200F, 2000F);
            parameters[3] = new EffectParam(frequencyRangeKey, "Frequency range key", 1000F, 10000F);
            parameters[4] = new EffectParam(masterKey, "Master volume", 0F, 10F);

            return parameters;
        }

        #endregion

    }
}